#############################################################################
#
# Just Another Package Builder http://code.google.com/p/japb
# (c)2011 Alexander Galanin
#
# Functions for starpack creation
#
# Tclkits should be placed into $tclkitDir/$platform-$arch
# This can be overrriden by replacing tclkitFile function
#
# $Id$
#
#############################################################################

package require Tcl 8.5
package require japb::filenode
package require japb::tcl

package provide japb::starpack 1.0

namespace eval japb {

namespace export starpack

namespace eval starpack {

namespace import ::japb::filenode
namespace import ::japb::tcl::makeTclIndex

variable tclsh
variable tclkitDir tclkit

namespace import [namespace parent]::dir
namespace import [namespace parent]::registerOption
namespace import [namespace parent]::registerPostParseCommand

registerOption -tclkitdir starpack::tclkitDir "Tclkit directory"
registerPostParseCommand [namespace code detectTclshForStarkit]

# Get tclkit file
# @param type type of runtime (console or gui)
# @param platform platform (windows or unix) and CPU arch (e.g. windows-x86)
proc tclkitFile {type platform} {
    variable tclkitDir

    set suffix [osExeSuffix [lindex [split $platform -] 0]]
    set kitexe [dict get {gui tclkit console tclkitsh} $type]
    file join $tclkitDir [string tolower $platform] $kitexe$suffix
}

# Create starpack by generation vfs dir and wrapping it to executable by using
# sdx. Type is defined by runtime type (gui or console), platform (windows or
# linux) and CPU architecture.
# @param name generated file name
# @param type starpack type: runtime, os, arch
# @param mainFile file to be copied to main.tcl
# @param args list of file nodes
# @return file node for generated starpack
proc starpack {name type mainFile args} {
    lassign $type runtimeType os arch

    set suffix [osExeSuffix $os]
    set runtime [tclkitFile $runtimeType $os-$arch]
    createAndWrapStarkit $name$suffix $runtime $mainFile {*}$args
}

# Create starkit by generation vfs dir and wrapping it by using sdx.
# @param name generated file name
# @param mainFile file to be copied to main.tcl
# @param args list of file nodes
# @return file node for generated starkit
proc starkit {name mainFile args} {
    createAndWrapStarkit $name.kit {} $mainFile {*}$args
}

# Generate starkit or starpack
# @param outname output file name
# @param runtime tuntime prefix or {} to generate starkit
# @param mainFile file to be copied to main.tcl
# @param args list of file nodes
# @return file node for generated file
proc createAndWrapStarkit {outname runtime mainFile args} {
    variable tclkitDir
    variable tclsh

    set vfs [dir starpack-$outname.vfs {*}$args \
        [filenode create $mainFile main.tcl] \
    ]

    # create lib directory and make index file in it
    set libdir [file join [filenode path $vfs] lib]
    file mkdir $libdir
    makeTclIndex $libdir

    set sdx [file join $tclkitDir sdx.kit]
    set out [filenode mkfile $outname]

    if {$runtime ne ""} {
        set runtimeArgs [list -runtime $runtime]
    } else {
        set runtimeArgs {}
    }
    #TODO: quiet mode
    exec $tclsh $sdx wrap [filenode path $out] \
        -vfs [filenode path $vfs] \
        {*}$runtimeArgs \
        >@stdout 2>@stderr
    # check that file is generated
    if {![file exists [filenode path $out]]} {
        error "No file generated ([filenode path $out])!"
    }
    # make file executable
    if {$::tcl_platform(platform) eq "unix"} {
        file attributes [filenode path $out] -permissions 00755
    }
    return $out
}

# Detech tclsh executable to use in starpack creation. This can not be the
# same executable as used as runtime for starpack due to sdx limitation.
proc detectTclshForStarkit {} {
    variable [namespace parent]::tempDir
    variable tclkitDir
    variable tclsh

    set tclkitDir [file normalize $tclkitDir]
    set exeSuffix [osExeSuffix $::tcl_platform(platform)]
    set exename [info nameofexecutable]

    # if we have starkit package...
    if {![catch {package require starkit}]} {
        # detect tclkit platform
        ::starkit::startup
        set tclkitsh [tclkitFile console [starkit::platform]]
        set tclsh [file join $tempDir tclsh$exeSuffix]
        # and try to use the corresponding executable from tclkit dir
        if {[file exists $tclkitsh]} {
            # If tclkitsh executable is found, copy it to temp dir to avoid
            # conflicts while using tclkitsh as wrapped file header.
            # return to skip showing of warning message
            set fileToCopy $tclkitsh
            set showWarning false
        } else {
            # script is possibly executed via tclkitsh
            # copy it to temporary dir
            set fileToCopy $exename
            set showWarning true
        }
        # File should be copied using OS' command because copying via
        # [file copy] may give strange results if there are Tcl VFS
        # mounted over file.
        if {$::tcl_platform(platform) eq "unix"} {
            exec cp $fileToCopy $tclsh
            file attributes $tclsh -permissions 00755
        } else {
            exec {*}[auto_execok copy] /b [file nativename $fileToCopy] [file nativename $tclsh]
        }
    } else {
        # hope that our tclsh can execute sdx.kit
        set tclsh $exename
        set showWarning true
    }
    if {$showWarning} {
        puts stderr "Warning: tclkitsh for current platform is not found. Using caller instead."
    }
}

# Get executable file extension for specified operating system
# @param os operating system
proc osExeSuffix {os} {
    if {[string tolower $os] eq "windows"} {
        return .exe
    } else {
        return {}
    }
}

namespace export \
    starpack \
    starkit \
    tclkitFile \
    osExeSuffix
namespace ensemble create
}
}
